﻿using Abp.Application.Navigation;
using Abp.Dependency;
using Abp.Domain.Entities;
using Abp.Domain.Repositories;
using Abp.Extensions;
using Abp.Runtime.Session;
using Microsoft.EntityFrameworkCore;
using Mt.Site.Core.Base.Reflection;
using Mt.Site.Core.ContentManagement.Entities;
using Mt.Site.Core.ContentManagement.Values;
using Mt.Site.Core.DiyContent.Form;
using Mt.Site.Core.DiyContent.Form.Entities;
using Mt.Site.Core.Navigations.Navigation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;

namespace Mt.Site.Core.ContentManagement.Impl
{
    public class ContentFrontendManager : IContentFrontendManager, ITransientDependency
    {
        private readonly IRepository<Content, Guid> _contentRepository;
        private readonly IRepository<ContentBody, long> _contentBodyRepository;
        private readonly IRepository<ContentField, long> _contentFieldRepository;
        private readonly IMenuContentHelper _menuContentHelper;
        private readonly INavigationManager _navigationManager;

        public IAbpSession AbpSession { get; set; }

        public ContentFrontendManager(
            IMenuContentHelper menuContentHelper,
            IRepository<Content, Guid> contentRepository,
            IRepository<ContentBody, long> contentBodyRepository,
            IRepository<ContentField, long> contentFieldRepository,
            INavigationManager navigationManager)
        {
            _menuContentHelper = menuContentHelper;
            _contentRepository = contentRepository;
            _contentBodyRepository = contentBodyRepository;
            _contentFieldRepository = contentFieldRepository;
            _navigationManager = navigationManager;

            AbpSession = NullAbpSession.Instance;
        }
        
        #region 泛型
        public async Task<List<T>> GetAllContentAsync<T>(Func<IQueryable<Content>, IQueryable<Content>> queryMethod, int maxCount = 0) where T : new()
        {
            var query = _contentRepository.GetAll();
            query = queryMethod(query);
            if (maxCount > 0)
            {
                query = query.Take(maxCount);
            }
            var list = await query.ToListAsync();

            var menu = _navigationManager.GetContentMenuCache(AbpSession.GetTenantId());
            var menuIdList = list
                .Select(c => c.MenuId)
                .Distinct()
                .Where(c => 
                { 
                    var item = menu.GetItemByAuxiliaryIdOrNull(c);
                    return item != null && !item.IsFrontendDisabled(); 
                })
                .ToList();
            var newList = list.Where(c => menuIdList.Contains(c.MenuId)).ToList();

            var result = new List<T>();
            foreach (var item in newList)
            {
                var item2 = GetItemByReflection<T>(item, null, null);
                result.Add(item2);
            }
            return result;
        }

        public async Task<T> GetContentByIdAsync<T>(long id) where T : new()
        {
            var entity = await _contentRepository.FirstOrDefaultAsync(c => c.AuxiliaryId == id && c.IsActive);
            if (entity == null)
            {
                throw new EntityNotFoundException(typeof(Content), id);
            }

            var bodyFields = await _contentBodyRepository.GetAllListAsync(c => c.ContentId == entity.Id);
            var fields = await _contentFieldRepository.GetAllListAsync(c => c.ContentId == entity.Id);

            return GetItemByReflection<T>(entity, bodyFields, fields);
        }

        public async Task<T> GetContentByIdPreaViewAsync<T>(Guid id) where T : new()
        {
            var entity = await _contentRepository.FirstOrDefaultAsync(c => c.Id == id);
            if (entity == null)
            {
                throw new EntityNotFoundException(typeof(Content), id);
            }

            var bodyFields = await _contentBodyRepository.GetAllListAsync(c => c.ContentId == entity.Id);
            var fields = await _contentFieldRepository.GetAllListAsync(c => c.ContentId == entity.Id);

            return GetItemByReflection<T>(entity, bodyFields, fields);
        }

        public async Task<long> GetLongIdAsync(Guid id) 
        {

            var query = from q in _contentRepository.GetAll()
                        where q.Id == id
                        select q.AuxiliaryId;

            return await query.FirstOrDefaultAsync();
        }

        public async Task<PagedContent<T>> GetContentListByPagedAsync<T>(
            Func<IQueryable<Content>, IQueryable<Content>> queryMethod, 
            int skipCount, int getCount) where T : new()
        {
            var query = _contentRepository.GetAll();
            query = queryMethod(query);

            var count = await query.CountAsync();
            var list = await query.Skip(skipCount).Take(getCount).ToListAsync();

            var result = new List<T>();
            foreach (var item in list)
            {
                var item2 = GetItemByReflection<T>(item, null, null);
                result.Add(item2);
            }

            return new PagedContent<T>
            {
                TotolCount = count,
                Items = result
            };
        }


        private T GetItemByReflection<T>(Content content,List<ContentBody> bodyFields,List<ContentField> fields) where T : new()
        {
            var t = new T();
            var properties = TypeHelper.GetPrimitiveProperties<T>();
            foreach (var propertie in properties)
            {
                var propertieKey = propertie.Key;
                var value = TypeHelper.GetPropertyValue(content, propertieKey);
                if (value != null)
                {
                    TypeHelper.SetPropertyValue(t, propertieKey, value);
                }
                else
                {
                    ContentBody item = null;
                    if (bodyFields != null)
                    {
                        item = bodyFields.FirstOrDefault(c => c.FieldName == propertieKey);
                        if (item != null)
                        {
                            TypeHelper.SetPropertyValue(t, propertieKey, item.Value);
                        }
                    }
                    if (item == null && fields != null)
                    {
                        var item2 = fields.FirstOrDefault(c => c.FieldName == propertieKey);
                        if (item2 != null)
                        {
                            TypeHelper.SetPropertyValue(t, propertieKey, item2.Value);
                        }
                    }
                }
            }

            return t;
        }

        #endregion

        #region Object


        private async Task<ContentConfig> GetConfig(long menuId)
        {
            return await _menuContentHelper.GetConfig(null, menuId);
        }

        public async Task<Dictionary<string, object>> GetAsync(long id)
        {
            var content = await _contentRepository.FirstOrDefaultAsync(c => c.AuxiliaryId == id && c.IsActive);
            if (content == null)
            {
                throw new EntityNotFoundException(typeof(Content), id);
            }
            var config = await GetConfig(content.MenuId);
            if(config == null)
            {
                throw new EntityNotFoundException(typeof(Content), id);
            }

            var result = new Dictionary<string, object>();
            var detailFields = config.Detail
                 .Where(ContentFieldsHelper.CheckDataPredicate())
                .ToList();
            //依据配置返回
            var bodyFields = await _contentBodyRepository.GetAllListAsync(c => c.ContentId == content.Id);
            var fields = await _contentFieldRepository.GetAllListAsync(c => c.ContentId == content.Id);
            bool hasAdded;

            foreach (var propertie in detailFields)
            {
                hasAdded = false;
                var propertieKey = propertie.FieldName.ToPascalCase();
                var value = TypeHelper.GetPropertyValue(content, propertieKey);
                if (value != null)
                {
                    hasAdded = true;
                    result.Add(propertieKey, value);
                }
                else
                {
                    ContentBody item = null;
                    if (bodyFields != null)
                    {
                        item = bodyFields.FirstOrDefault(c => c.FieldName == propertieKey);
                        if (item != null)
                        {
                            hasAdded = true;
                            result.Add(propertieKey, item.Value);
                        }
                    }
                    if (item == null && fields != null)
                    {
                        var item2 = fields.FirstOrDefault(c => c.FieldName == propertieKey);
                        if (item2 != null)
                        {
                            hasAdded = true;
                            result.Add(propertieKey, item2.Value);
                        }
                    }
                }
                if (!hasAdded) //正常不会出现
                {
                    var defaultValue = TypeHelper.GetDefaultValue(propertie.FieldType, propertie.DefaultValue);
                    result[propertieKey] = defaultValue;
                }
            }

            foreach (var propertieKey in ContentManager._specialFields)
            {
                var value = TypeHelper.GetPropertyValue(content, propertieKey);
                result[propertieKey] = value;
            }

            return result;
        }

        #endregion

        public async Task<int> UpdatePv(long id)
        {
            var entity = await _contentRepository.FirstOrDefaultAsync(c => c.AuxiliaryId == id);
            if (entity != null)
            {
                entity.Hit += 1;

                return entity.Hit;
            }
            return 0;
        }

        public async Task<PreAndNextItemId> GetPreAndNextLink(string sorting, long menuId, Guid? relateId, Guid id)
        {
            long LongNowTime = DateTime.Now.ToUnixTimeByMilliseconds();
            var query = _contentRepository.GetAll()
                .Where(c=> c.MenuId == menuId && c.IsActive && c.RelateId == relateId && LongNowTime >= c.Time1);
            query = GetOrderedQuery(sorting, query);
            var idList = await query.Select(c => new { c.Id, c.AuxiliaryId }).ToListAsync();

            var result = new PreAndNextItemId();
            var index = idList.FindIndex(t =>t.Id==id);
            if (index >= 0)
            {
                if (index < idList.Count - 1)
                {
                    //Next
                    result.NextId = idList[index + 1].Id;
                    result.NextAuxiliaryId= idList[index + 1].AuxiliaryId;
                }
                if (index > 0)
                {
                    //Pre
                    result.PreId = idList[index - 1].Id;
                    result.PreAuxiliaryId = idList[index - 1].AuxiliaryId;
                }
            }
            return result;
        }

        public IQueryable<Content> GetOrderedQuery(string sorting, IQueryable<Content> q)
        {
            q = !sorting.IsNullOrEmpty()
                ? q.OrderBy(sorting)
                : q.OrderByDescending(t => t.Order).ThenByDescending(t => t.PublishTime);
            return q;
        }
    }
}
